#ifndef _PRENSTAR_ASSERT_H_
#define _PRENSTAR_ASSERT_H_

#include <cstdio>
#include <cstdlib>
#include <sstream>


#ifndef NDEBUG
# define _prenstar_display_error(__what, __message_if_false...) \
  std::fprintf(stderr, "In \033[1;34mfile %s\033[0m at \033[1;33mline %d\033[0m, assertion \033[1;32m%s\033[0m failed: \033[1;31m", __FILE__, __LINE__, #__what); \
  std::fprintf(stderr, __message_if_false);\
  std::fprintf(stderr, "\033[0m\n"); \
  std::abort() \

# define prenstar_assert(__what, __message_if_false...) \
  do { \
    if (!(__what))\
    { \
      _prenstar_display_error(__what, __message_if_false); \
    } \
  } while (false)

# define prenstar_assert_nullptr(__first) \
  do { \
  if (__first) { \
    _prenstar_display_error(is_nullptr(__first), \
        #__first ": 0x%X", __first); \
}} while (false)

# define prenstar_assert_not_nullptr(__first) \
  do { \
  if (!(__first)) { \
    _prenstar_display_error(is_not_nullptr(__first), \
        #__first ": 0x%X", __first); \
}} while (false)

# define prenstar_assert_true(__first) \
  do { \
  if (!(__first)) { \
    _prenstar_display_error(is_true(__first), \
        #__first ": %s", (__first) ? "true" : "false"); \
}} while (false)

# define prenstar_assert_false(__first) \
  do { \
  if (__first) { \
    _prenstar_display_error(is_false(__first), \
        #__first ": %s", (__first) ? "true" : "false"); \
  }} while (false)

# define prenstar_assert_eq(__first, __second) \
  do { \
  if (!((__first) == (__second))) { \
    std::stringstream __prenstar_ss1; \
    std::stringstream __prenstar_ss2; \
    __prenstar_ss1 << __first; \
    __prenstar_ss2 << __second; \
    _prenstar_display_error(__first == __second, \
        #__first ": %s, " #__second ": %s", \
        __prenstar_ss1.str().c_str(), \
        __prenstar_ss2.str().c_str()); \
}} while (false)

# define prenstar_assert_ne(__first, __second) \
  do { \
    if (!((__first) != (__second))) { \
      std::stringstream __prenstar_ss1; \
      std::stringstream __prenstar_ss2; \
      __prenstar_ss1 << __first; \
      __prenstar_ss2 << __second; \
      _prenstar_display_error(__first != __second, \
          #__first ": %s, " #__second ": %s", \
          __prenstar_ss1.str().c_str(), \
          __prenstar_ss2.str().c_str()); \
  }} while (false)

# define prenstar_assert_implies(__first, __second) \
  do { \
    if (!(!(__first) || (__second))) { \
      _prenstar_display_error(__first ==> __second, \
          #__first ": %s, " #__second ": %s", \
          (__first) ? "true" : "false", \
          (__second) ? "true" : "false"); \
  }} while (false)


# define prenstar_assert_lt(__first, __second) \
  do { \
    if (!(__first < __second)) { \
      std::stringstream __prenstar_ss1; \
      std::stringstream __prenstar_ss2; \
      __prenstar_ss1 << __first; \
      __prenstar_ss2 << __second; \
      _prenstar_display_error(__first < __second, \
          #__first ": %s, " #__second ": %s", \
          __prenstar_ss1.str().c_str(), \
          __prenstar_ss2.str().c_str()); \
  }} while (false)

# define prenstar_assert_le(__first, __second) \
  do { \
    if (!(__first <= __second)) { \
      std::stringstream __prenstar_ss1; \
      std::stringstream __prenstar_ss2; \
      __prenstar_ss1 << __first; \
      __prenstar_ss2 << __second; \
      _prenstar_display_error(__first <= __second, \
          #__first ": %s, " #__second ": %s", \
          __prenstar_ss1.str().c_str(), \
          __prenstar_ss2.str().c_str()); \
  }} while (false)

# define prenstar_assert_gt(__first, __second) \
  do { \
    if (!(__first > __second)) { \
      std::stringstream __prenstar_ss1; \
      std::stringstream __prenstar_ss2; \
      __prenstar_ss1 << __first; \
      __prenstar_ss2 << __second; \
      _prenstar_display_error(__first > __second, \
          #__first ": %s, " #__second ": %s", \
          __prenstar_ss1.str().c_str(), \
          __prenstar_ss2.str().c_str()); \
  }} while (false)

# define prenstar_assert_ge(__first, __second) \
  do { \
    if (!(__first >= __second)) { \
      std::stringstream __prenstar_ss1; \
      std::stringstream __prenstar_ss2; \
      __prenstar_ss1 << __first; \
      __prenstar_ss2 << __second; \
      _prenstar_display_error(__first >= __second, \
          #__first ": %s, " #__second ": %s", \
          __prenstar_ss1.str().c_str(), \
          __prenstar_ss2.str().c_str()); \
  }} while (false)

#else
# define prenstar_assert(__what, __message_if_false...)
# define prenstar_assert_eq(__first, __second)
# define prenstar_assert_ne(__first, __second)
# define prenstar_assert_lt(__first, __second)
# define prenstar_assert_le(__first, __second)
# define prenstar_assert_gt(__first, __second)
# define prenstar_assert_ge(__first, __second)
# define prenstar_assert_implies(__first, __second)
#endif // NDEBUG

#endif // _PRENSTAR_ASSERT_H_
